/*
 * 版权所有 (C) 2015 知启蒙(ZHIQIM) 保留所有权利。[遇见知启蒙，邂逅框架梦]
 * 
 * https://zhiqim.org/project/zhiqim_framework/zhiqim_httpd.htm
 *
 * Zhiqim Httpd is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *          http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */
package org.zhiqim.httpd.context.config;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

import org.zhiqim.httpd.context.ZmlConfig;
import org.zhiqim.httpd.context.ZmlContextConstants;
import org.zhiqim.kernel.annotation.AnNullable;
import org.zhiqim.kernel.model.maps.LinkedMapSV;
import org.zhiqim.kernel.util.Asserts;
import org.zhiqim.kernel.util.Lists;
import org.zhiqim.kernel.util.Objects;
import org.zhiqim.kernel.util.Replaces;
import org.zhiqim.kernel.util.Validates;
import org.zhiqim.zml.Zmls;

/**
 * ZAction表，目标是把精确匹配和模糊匹配区分开来
 * 1. exactMap      精确匹配表
 * 2. fuzzyMap      模糊匹配表
 *
 * @version v1.0.0 @author zhichenggang 2016-12-31 新建与整理
 */
public class ZActionMap implements ZmlContextConstants
{
    private final ZmlConfig zmlConfig;
    private final ZConfig config;
    
    private LinkedMapSV<ZAction> exactMap = new LinkedMapSV<>();
    private ArrayList<ZAction> fuzzyList = new ArrayList<>();
    
    public ZActionMap(ZmlConfig zmlConfig, ZConfig config)
    {
        this.zmlConfig = zmlConfig;
        this.config = config;
    }
    
    /**********************************************************************/
    //list & add & toString
    /**********************************************************************/
    
    /** 获取精确表 */
    public Collection<ZAction> getExactActionList()
    {
        return exactMap.values();
    }
    
    /** 获取模糊表 */
    public Collection<ZAction> getFuzzyActionList()
    {
        return fuzzyList;
    }
    
    /**
     * 添加一个ZAction
     * 
     * @param path      路径，精确或模糊pattern
     * @param action    ZAction类
     */
    public void addAction(ZAction action)
    {
        action.setConfig(config);
        String path = action.getPath();
        if (path.indexOf("*") == -1)
            exactMap.put(path, action);
        else
            fuzzyList.add(action);
    }
    
    /** 删除一个ZAction */
    public void removeAction(String path)
    {
        if (path.indexOf("*") == -1)
        {//精确
            exactMap.remove(path);
            return;
        }
            
        //模糊
        for (Iterator<ZAction> it=fuzzyList.iterator();it.hasNext();)
        {
            ZAction action = it.next();
            if (path.equals(action.getPath()))
            {//只会有一个，找到删除即可退出
                it.remove();
                return;
            }
        }
    }
    
    /** 检查变量名和模糊排序 */
    public void chkVariableAndSort()
    {
        //1.检查精确的变量
        LinkedMapSV<ZAction> newExactMap = new LinkedMapSV<>();
        for (ZAction zAction : exactMap.values())
        {
            ZAction newAction = chkVariable(zAction);
            newExactMap.put(newAction.getPath(), newAction);
        }
        
        exactMap.clear();
        exactMap = newExactMap;
        
        //2.检查模糊的变量
        ArrayList<ZAction> newFuzzyList = new ArrayList<>();
        for (ZAction zAction : fuzzyList)
        {
            ZAction newAction = chkVariable(zAction);
            newFuzzyList.add(newAction);
        }
        
        fuzzyList.clear();
        fuzzyList = newFuzzyList;
        fuzzyList.trimToSize();
        
        //3.对模糊的包含关系重新排序，采用冒泡方案
        int size = fuzzyList.size();
        for (int i=0;i<size;)
        {
            int k = i;
            ZAction pa = fuzzyList.get(i);
            for (int j=k+1;j<size;j++)
            {
                ZAction sa = fuzzyList.get(j);
                if (Lists.getMatchList(sa.getPath(), pa.getPath()) != null)
                {//上面的大于下面的时交换
                    fuzzyList.set(k, sa);
                    fuzzyList.set(j, pa);
                    k = j;//p移到了j的位置
                }
            }
            
            if (k == i)
            {//没匹配到的表明比下面的小，索引向下移动
                i++;
            }
        }
    }
    
    /** 检查变量名 */
    private ZAction chkVariable(ZAction zAction)
    {
        String path = zAction.getPath();
        if (path.contains("${") || path.contains("<#"))
        {
            try
            {
                zAction.setPath(Zmls.parseContext(path, zmlConfig));
            }
            catch (Exception e)
            {
                throw Asserts.exception("action配置[%s]，path变量解析失败", path);
            }
        }
        
        String redirect = zAction.getRedirect();
        if (Validates.isNotEmpty(redirect) && path.contains("${") || path.contains("<#"))
        {
            try
            {
                zAction.setRedirect(Zmls.parseContext(redirect, zmlConfig));
            }
            catch (Exception e)
            {
                throw Asserts.exception("action配置[%s]，redirect变量解析失败", path);
            }
        }
        
        String forward = zAction.getForward();
        if (Validates.isNotEmpty(forward) && path.contains("${") || path.contains("<#"))
        {
            try
            {
                zAction.setForward(Zmls.parseContext(forward, zmlConfig));
            }
            catch (Exception e)
            {
                throw Asserts.exception("action配置[%s]，forward变量解析失败", path);
            }
        }
        
        return zAction;
    }
    
    /** toString */
    public String toString()
    {
        StringBuilder strb = new StringBuilder();
        for (ZAction action : fuzzyList)
        {
            strb.append(_FOUR_).append(action.toString()).append(_BR_);
        }
        for (ZAction action : exactMap.values())
        {
            strb.append(_FOUR_).append(action.toString()).append(_BR_);
        }
        
        return strb.toString();
    }
    
    /**********************************************************************/
    //通过类名方式
    /**********************************************************************/
    
    /**
     * 获取精确匹配的ZAction的path
     * 
     * @param clazz         传入的类名
     * @return              path
     */
    @AnNullable
    public ZAction getExactActionByClass(String clazz)
    {
        if (clazz == null)
            return null;
        
        for (ZAction action : exactMap.values())
        {
            if (clazz.equals(action.getClazz()))
                return action;
        }
        
        return null;
    }
    
    /**
     * 获取模糊匹配的ZAction的path
     * 
     * @param clazz         传入的类名
     * @return              path
     */
    @AnNullable
    public ZAction getFuzzyActionByClass(String clazz)
    {
        if (clazz == null)
            return null;
        
        for (ZAction action : fuzzyList)
        {
            if (clazz.equals(action.getClazz()))
                return action;
        }
        
        return null;
    }
    
    /**********************************************************************/
    //通过 path 获取 ZAction
    /**********************************************************************/
    
    /**
     * 获取精确匹配的ZAction
     * 
     * @param path  传入的上下文环境下的路径
     * @return      ZAction
     */
    @AnNullable
    public ZAction getExactAction(String path)
    {
        return exactMap.get(path);
    }
    
    /**
     * 获取模糊匹配的ZAction
     * 
     * @param path  传入的上下文环境下的路径
     * @return      ZAction
     */
    @AnNullable
    public ZAction getFuzzyAction(String path)
    {
        for (ZAction action : fuzzyList)
        {
            String p = action.getPath();
            List<String> matchList = Lists.getMatchList(path, p);
            if (matchList == null)
                continue;
            
            return getActionMatchParam(p, action, path, matchList);
        }
        
        return null;
    }
    
    /** 获取含通匹符的Action */
    private ZAction getActionMatchParam(String path, ZAction action, String pathInContext, List<String> paramList)
    {
        //生成新的Action配置
        ZAction newAction = Objects.copy(action, new ZAction());
        newAction.setPath(pathInContext);//重置path为新的pathInContext
        
        for (int i=0;i<paramList.size();i++)
        {//对参数表替换{}
            String regex = "{"+(i+1)+"}";
            String replacement = paramList.get(i);
            
            newAction.setInterceptor(Replaces.replaceAllEscape(newAction.getInterceptor(), regex, replacement));
            newAction.setForward(Replaces.replaceAllEscape(newAction.getForward(), regex, replacement));
            newAction.setRedirect(Replaces.replaceAllEscape(newAction.getRedirect(), regex, replacement));
            newAction.setView(Replaces.replaceAllEscape(newAction.getView(), regex, replacement));
            newAction.setInclude(Replaces.replaceAllEscape(newAction.getInclude(), regex, replacement));
            newAction.setClazz(Replaces.replaceAllEscape(newAction.getClazz(), regex, replacement));
            newAction.setMethod(Replaces.replaceAllEscape(newAction.getMethod(), regex, replacement));
            newAction.setSuccess(Replaces.replaceAllEscape(newAction.getSuccess(), regex, replacement));
        }
        
        return newAction.setMatchList(paramList);
    }
}
